/*
 * sched.c - initializes struct for task 0
 */

#include <sched.h>
#include <mm.h>
#include <list.h>
#include <io.h>
#include <files.h>


struct protected_task_struct task[NR_TASKS]
  __attribute__((__section__(".data.task")));

void init_task0(void)
{
	/** Initializing global variables */
	current_quantum = QUANTUM;
	pid = 0;
	INIT_LIST_HEAD(&runqueue);
	
/* Initializes paging for the process 0 adress space */
	set_user_pages(&task[0].t.task);
	set_cr3();

	/*** Initializing task_struct of task 0 */
	task[0].t.task.pid = pid;
	task[0].t.task.state = RUN;
	task[0].t.task.quantum = QUANTUM;
	task[0].t.task.total_trans = 0;
	task[0].t.task.total_tics = 0;
	 
	/*** Saving the physical pages in task_struct of process 0 */
	save_frames_P0();
	
	/*** Setting PIDs of all tasks rather than task 0 to -1, marking their task_structs  as free*/
	int i;
	for (i=1;i<NR_TASKS;i++)
	{	
		task[i].t.task.pid = -1;
	}
	
	/*** Adding task 0 to the runqueue */
	list_add(&task[0].t.task.list,&runqueue);


	/*** Initializing the semaphore array */
	for (i=0; i<NR_SEM; i++){
		sem_config(i);
	}

}

/*** Get the address of task_struct of current process */
struct task_struct * current()
{ 
  struct task_struct *p;
    __asm__ __volatile__(
		"movl $0xffff000,%%ecx\n\t" //moving a mask for 12 less significant bits (2^12 = 4096 - page size) into ecx
		"andl %%esp,%%ecx\n\t"  //applying the mask
		"movl %%ecx,%0\n\t" //putting the resulting address to p
		: "=g"(p)
		:  );
  return p;
}

/*** Get the adress of task_struct by its list_head */
struct task_struct * list_head_to_task_struct(struct list_head *l)
{
  return list_entry(l,struct task_struct,list);
  /* l - the &struct list_head pointer.
   * struct task_struct - the type of the struct this is embedded in.
   * list -	the name of the list_struct within the struct task_struct.
   */

}

/*** Finding free task_struct in the array:
* if it finds a free task_struct - it retuns its number, if it doesn't find anything - it returns -1*/
int find_free_task()
{
	int i;
	for (i=1;i<NR_TASKS;i++)
	{
		if (task[i].t.task.pid<0) return i;
	}
	return -1;
}


/*** Task switch */
void task_switch(union task_union * t)
{
	/*** Update the TSS to make it point to the new task system stack */
	tss.esp0 = (DWord)&(t->stack[KERNEL_STACK_SIZE]);

	/*** Change the system stack of the new process */
	__asm__ __volatile__(
		"movl %0,%%esp\n\t"
		"subl $64,%%esp\n\t" //16 entries in the stack
		: 
		: "g"(tss.esp0));

	/*** Change the user address space by updating the page table to make the data+stack user pages of the new task accessible */
	int i = 0;
	for (i=0; i<NUM_PAG_DATA;i++)
	{
		set_ss_pag(PAG_LOG_INIT_DATA+i,t->task.frames[i]);
	}

	/*** Flushing TLB after changes in the page table */
	set_cr3();
	

	/*** If the task_switch was called from the interrupt, send EOI */
	if(switch_task_by_interruption == 1)
	{
		switch_task_by_interruption = 0;
  		__asm__ __volatile__(
			"movb $0x20, %al\n\t"
			"outb %al, $0x20\n\t"
		); 
  	}

	/*** Restore the registers */

	__asm__ __volatile__(
		"popl %%ebx\n\t"
		"popl %%ecx\n\t"
		"popl %%edx\n\t"
		"popl %%esi\n\t"
		"popl %%edi\n\t"
		"popl %%ebp\n\t"
		"popl %%eax\n\t"
		"popl %%ds\n\t"
		"popl %%es\n\t"
		"popl %%fs\n\t"
		"popl %%gs\n\t"
		:::"memory"
	);

	__asm__ __volatile__(
		"iret\n\t"
	);

}
/*** Round robin scheduling policy */
void scheduling()
{
	if (current_quantum==0)
	{		switch_task_by_interruption=1;
		round_robin();
	}
}
	
void round_robin()
{
	struct task_struct* proces;
	struct list_head* aux;

//* Outgoing process management *//
	
	aux = list_first(&runqueue); //getting the current procces
	proces = list_head_to_task_struct(aux);

	current()->state=READY; //change the state of the outgoing procces to ready
	list_del(list_first(&runqueue)); //deleting from the front of the queue
	list_add_tail(aux, &runqueue); //and add it again at the end of the queue

//*Ingoing process preparation *//
	aux = list_first(&runqueue);
	proces = list_head_to_task_struct(aux);
	proces->state=RUN;		//why can pass this parameters
	proces->total_trans++;
	current_quantum=proces->quantum;

//* switch the task to finish*//
	task_switch((union task_union*)proces);//currently in development
	
}


/*** Semafores initialization */
void sem_config(int n_sem){
	sem[n_sem].owner=-1;
	sem[n_sem].counter=0;
	//sem[n_sem].waiting=0;
	INIT_LIST_HEAD(&sem[n_sem].sem_list);
}
	
